/*
 if using msvc, compile with /Tp not /Tc
*/

#ifdef _MSC_VER
	#pragma comment(lib, "glfwdll.lib")
	#pragma comment(lib, "opengl32.lib")
	#pragma comment(lib, "glew32.lib")
	#pragma comment(lib, "glu32.lib")
	#pragma comment(lib, "soil.lib")
	
	#define snprintf _snprintf
#else
	#include <windef.h>
	#define __MINGW32__ // for glew.h, need int types
#endif

#include <gl\glew.h>
#define GLFW_NO_GLU
#include <gl\glfw.h>
#include <gl\soil.h>

#include <stdlib.h>
#include <math.h>
#include <stdio.h>

struct particle_t{
	float x,y;
};

float rnd()
{
	return rand() / (float) RAND_MAX;
}

struct globals_t{
	int xres, yres;
	int mousx, mousy;
};

struct globals_t GLOBALS;

void drawDeJong()
{
		float x = rnd()*0.5f - 0.25f;
		float y = rnd()*0.5f - 0.25f;
		
		// float a = +1.4f;
		// float b = +2.3f;
		// float c = -2.4f;
		// float d = -1.57f;
		float a = +1.4f * GLOBALS.mousx/(float)GLOBALS.xres;
		float b = +2.3f;
		float c = -2.4f;
		float d = -1.57f;
		
		int iters = 1e5;
			
		glBegin(GL_POINTS);
		int i=0;
		for(i=0; i<iters; i++)
		{
			float xn = sin(a*y) - cos(b*x);
			float yn = sin(c*x) - cos(d*y);
			x = xn;
			y = yn;
			
			float scaler = 0.8f * xres * 0.25;

			float plotx = GLOBALS.xres/2.0f + x*scaler;
			float ploty = GLOBALS.yres/2.0f - y*scaler;
			
			float r1 = plotx/GLOBALS.xres * 255.0f;
			float g1 = i * 255.0f / (float) iters;
			float b1 = ploty/GLOBALS.yres * 255.0f;
			
			glColor4ub(r1,g1,b1,100);
			glVertex2f( plotx, ploty );
		}
		glEnd();
		
}

void drawQuadratic()
{
   float constants[28+65];
   for(i=65; i<63+28; i++)
   {
      constants[i] = -1.2 + 0.1 * (i-65);
   }
   
   float a0 = constants['M'];
   float a1 = constants['C'];
   float a2 = constants['R'];
   float a3 = constants['B'];
   float a4 = constants['I'];
   float a5 = constants['P'];
   
   float b0 = constants['O'];
   float b1 = constants['P'];
   float b2 = constants['H'];
   float b3 = constants['T'];
   float b4 = constants['B'];
   float b5 = constants['N'];
   
   float x = 0.1;
   float y = rnd();
   
	int iters = 1e5;
		
	glBegin(GL_POINTS);
	int i=0;
	for(i=0; i<iters; i++)
	{
   
	   float xn = a0 + a1*x + a2*x*x + a3*x*y + a4*y + a5*y*y;
	   float yn = b0 + b1*x + b2*x*x + b3*x*y + b4*y + b5*y*y;
		   
	   x = xn;
	   y = yn;
	   
	   float scaler = 0.8f * globals.xres * 0.25;
	   float plotx = globals.xres * 0.5 + x * scaler;
	   float ploty = globals.yres * 0.5 + y * scaler;
	}
	glEnd();
   
}


int main()
{
	glfwInit();
	
	// xres, yres, redbits, greenbits, bluebits
	// Alphabits, depthbits and stencilbits 
	GLOBALS.xres = 800;
	GLOBALS.yres = 800;
	glfwOpenWindow(GLOBALS.xres,GLOBALS.yres,0,0,0, 0,0,0, GLFW_WINDOW);
	
	glewInit();
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, GLOBALS.xres, GLOBALS.yres, 0, -1,1);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	GLuint tex_2d = SOIL_load_OGL_texture
		(
			"dot.png",
			SOIL_LOAD_AUTO,
			SOIL_CREATE_NEW_ID,
			SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT
		);
		
	struct particle_t particles[100];
	
	if( GL_POINT_SPRITE )
	{
		glEnable(GL_POINT_SPRITE);
		//glActiveTexture(GL_TEXTURE0);
		glEnable( GL_TEXTURE_2D );
		glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); //Generates tex coords for Point Sprite
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);  //GL_MODULATE, GL_DECAL, GL_BLEND, Or GL_REPLACE.
	}
	else
	{
		puts("program requires GL_POINT_SPRITE\n");
		glfwCloseWindow();
	}
	
	

	char charBuf[70];
	double time = 0;
	double dt;
    
	while( glfwGetWindowParam(GLFW_OPENED) )
	{
		double now = glfwGetTime();
		dt = now - time;
		time = now;
		
		int fps = 1.0 / dt;
		snprintf(charBuf,70, "Strange Attractor explorer prog by Torb,  %i fps", fps);
		
		glfwSetWindowTitle(charBuf);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT  );
		
			
		glfwGetMousePos( &GLOBALS.mousx, &GLOBALS.mousy );
		
		glColor4ub(255,255,255,128);
		glEnable(GL_BLEND);
		//glBlendFunc(GL_ONE, GL_ONE);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE); // aka lightblend
		
		glPointSize(10.0f);
		
		//settex
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, tex_2d);
		
		//drawDeJong();
		drawQuadratic();
		
		
		glColor4ub(255,255,255,128);
		
		glBegin(GL_POINTS);
		int i=0;
		for(i=0; i<100-1; i++)
		{
			particles[i].x = particles[i+1].x; 
			particles[i].y = particles[i+1].y; 
			glVertex2f( particles[i].x, particles[i].y );
		}
		glEnd();
			
			
		
		
		particles[99].x = GLOBALS.mousx;
		particles[99].y = GLOBALS.mousy;

		glDisable(GL_TEXTURE_2D);
		glColor3ub(255,255,255);
		
		GLenum err;
		while( (err = glGetError() ) != GL_NO_ERROR )
		{
			
			
			switch( err )
			{
			case GL_INVALID_ENUM:
				puts("GL_INVALID_ENUM, did you type GL_BEGIN(GL_LINE), instead of GL_LINES?");
				break;
			case GL_INVALID_VALUE:
				puts("GL_INVALID_VALUE, did you pass float where and int was expected?");
				break;
			case GL_INVALID_OPERATION:
				puts( "GL_INVALID_OPERATION");
				break;
			case GL_STACK_OVERFLOW:
				puts( "GL_STACK_OVERFLOW, stray push?");
				break;
			case GL_STACK_UNDERFLOW:
				puts( "GL_STACK_UNDERFLOW, stray pop?");
				break;
			case GL_OUT_OF_MEMORY:
				puts( "GL_OUT_OF_MEMORY");
				break;
			case GL_TABLE_TOO_LARGE:
				puts( "GL_TABLE_TOO_LARGE");
				break;
			case GL_INVALID_FRAMEBUFFER_OPERATION:
				puts( "GL_INVALID_FRAMEBUFFER_OPERATION");
				break;
			default:
				puts( "unknow gl error");
				break;
			}
			
			err = glGetError();
		}

		glfwSwapBuffers();
	}
	
	glfwTerminate();

}